1 module hip.hiprenderer.backend.metal.mtlrenderer;
2 
3 version(AppleOS):
4 
5 import metal;
6 import hip.hiprenderer.renderer;
7 import hip.windowing.window;
8 import hip.hiprenderer.backend.metal.mtlshader;
9 import hip.hiprenderer.backend.metal.mtlvertex;
10 import hip.hiprenderer.backend.metal.mtltexture;
11 
12 
13 
14 
15 
16 MTLCompareFunction fromHipDepthTestingFunction(HipDepthTestingFunction fn)
17 {
18     final switch(fn) with(HipDepthTestingFunction)
19     {
20         case Always: return MTLCompareFunction.Always;
21         case Never: return MTLCompareFunction.Never;
22         case Equal: return MTLCompareFunction.Equal;
23         case NotEqual: return MTLCompareFunction.NotEqual;
24         case Less: return MTLCompareFunction.Less;
25         case LessEqual: return MTLCompareFunction.LessEqual;
26         case Greater: return MTLCompareFunction.Greater;
27         case GreaterEqual: return MTLCompareFunction.GreaterEqual;
28     }
29 }
30 
31 package MTLCommandBuffer defaultCommandBuffer(MTLCommandQueue cQueue)
32 {
33     static MTLCommandBufferDescriptor desc;
34     if(desc is null)
35     {
36         desc = MTLCommandBufferDescriptor.alloc.initialize;
37         desc.errorOptions = MTLCommandBufferErrorOption.EncoderExecutionStatus;
38     }
39     return cQueue.commandBuffer(desc);
40 }
41 
42 private string getGPUFamily(MTLDevice device)
43 {
44     string ret = "";
45     static foreach(mem; __traits(allMembers, MTLGPUFamily))
46     {
47         if(device.supportsFamily(__traits(getMember, MTLGPUFamily, mem)))
48         {
49             if(ret != "") ret~= ", ";
50             ret~= mem;
51         }
52     }
53     if(ret == "")
54         return "unknown";
55     return ret;
56 }
57 
58 /** 
59  * Used to be sent as a way to interface with ShaderVar uniforms.
60  * Whenever needing to bind more than one texture.
61  */
62 package struct HipMTLShaderVarTexture
63 {
64     MTLTexture[16] textures;
65     MTLSamplerState[16] samplers;
66 }
67 
68 class HipMTLRenderer : IHipRendererImpl
69 {
70     MTKView view;
71     MTLDevice device;
72     MTLCommandBuffer cmdBuffer;
73     MTLCommandQueue cmdQueue;
74     MTLRenderPassDescriptor renderPassDescriptor;
75     MTLRenderCommandEncoder cmdEncoder;
76     MTLPrimitiveType primitiveType;
77 
78     package MTLArgumentBuffersTier argsTier;
79 
80     //Descriptors
81         MTLDepthStencilDescriptor depthStencilDescriptor;
82     //
83 
84     public bool init(HipWindow window)
85     {
86         view = cast(MTKView)window.MTKView;
87         device = view.device();
88         cmdQueue = device.newCommandQueue();
89         cmdQueue.label = "HipremeRendererQueue".ns;
90         // argsTier = device.argumentBuffersSupport;
91         ///Experimental Tier2 conditionals support, since I can't test them.
92         argsTier = MTLArgumentBuffersTier.Tier1;
93 
94         import hip.console.log;
95         {
96             MTLCompileOptions _temp = MTLCompileOptions.alloc.ini;
97 
98             loglnInfo(
99                 "GPU Name: ", device.name, "\n",
100                 "GPU Family: ", getGPUFamily(device), "\n",
101                 "Metal Version: ", _temp.languageVersion, "\n",
102                 "ArgumentBuffer: ", argsTier
103             );
104             _temp.dealloc;
105         }
106 
107 
108         depthStencilDescriptor = MTLDepthStencilDescriptor.alloc.ini;
109         renderPassDescriptor = view.currentRenderPassDescriptor;
110         if(renderPassDescriptor is null)
111             throw new Error("Could not get a render pass descriptor.");
112         renderPassDescriptor.depthAttachment.clearDepth = 1.0;
113 
114         hiplog(renderPassDescriptor.depthAttachment.level);
115         renderPassDescriptor.depthAttachment.loadAction = MTLLoadAction.Clear;
116 
117         return cmdQueue !is null;
118     }
119 
120     /** 
121      * Util for creating MTLResourceOptions.StorageModePrivate buffer with initial data.
122      */
123     package static MTLBuffer createPrivateBufferWithData(MTLCommandQueue cQueue, const(void)* data, size_t size)
124     { 
125         MTLDevice d = cQueue.device;
126         MTLBuffer temp = d.newBuffer(data, size, MTLResourceOptions.StorageModeShared);
127         MTLBuffer ret = d.newBuffer(size, MTLResourceOptions.StorageModePrivate);
128         MTLCommandBuffer _cmdBuffer = cQueue.defaultCommandBuffer();
129         MTLBlitCommandEncoder _cmdEncoder = _cmdBuffer.blitCommandEncoder;
130 
131         _cmdEncoder.copyFromBuffer(temp, 0, ret, 0, size);
132         _cmdEncoder.endEncoding();
133         _cmdBuffer.commit();
134         _cmdBuffer.waitUntilCompleted();
135         if(_cmdBuffer.error)
136         {
137             NSLog("Command Buffer Error %@".ns, _cmdBuffer.error);
138         }
139         return ret;
140     }
141 
142 
143     public bool initExternal()
144     {
145         return bool.init; // TODO: implement
146     }
147 
148     public bool isRowMajor(){return true;}
149     void setErrorCheckingEnabled(bool enable = true){}
150     public Shader createShader()
151     {
152         return new Shader(new HipMTLShader(device, this));
153     }
154 
155     ShaderVar* createShaderVar(ShaderTypes shaderType, UniformType uniformType, string varName, size_t length)
156     {
157         if(argsTier == MTLArgumentBuffersTier.Tier1) return null;
158         
159         switch(uniformType) with(UniformType)
160         {
161             case texture_array:
162             {
163                 size_t single = (MTLTexture.sizeof + MTLSamplerState.sizeof);
164                 return ShaderVar.createBlackboxed(shaderType, varName, uniformType, length*single, single);
165             }
166             default: return null;
167         }
168     }
169 
170 
171 
172     public IHipFrameBuffer createFrameBuffer(int width, int height)
173     {
174         return IHipFrameBuffer.init; // TODO: implement
175     }
176 
177     public IHipVertexArrayImpl createVertexArray()
178     {
179         return new HipMTLVertexArray(device, this);
180     }
181 
182     public IHipTexture createTexture()
183     {
184         return new HipMTLTexture(device,cmdQueue, this);
185     }
186 
187     public IHipVertexBufferImpl createVertexBuffer(size_t size, HipBufferUsage usage)
188     {
189         return new HipMTLVertexBuffer(device, cmdQueue, size, usage);
190     }
191 
192     package pragma(inline, true) MTLRenderCommandEncoder getEncoder() { return cmdEncoder; }
193 
194     public IHipIndexBufferImpl createIndexBuffer(index_t count, HipBufferUsage usage)
195     {
196         return new HipMTLIndexBuffer(device, cmdQueue, count, usage);
197     }
198 
199     public int queryMaxSupportedPixelShaderTextures()
200     {
201         return 8;///Max used in SpritebatchShader.
202     }
203 
204     public void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255)
205     {
206         renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(
207             r/255.0f,
208             g/255.0f,
209             b/255.0f,
210             a/255.0f
211         );
212     }
213 
214     public void setViewport(Viewport v)
215     {
216         cmdEncoder.setViewport(MTLViewport(v.x, v.y, v.width, v.height, 0, 1));
217     }
218 
219     public bool setWindowMode(HipWindowMode mode)
220     {
221         return bool.init; // TODO: implement
222     }
223 
224     public bool isBlendingEnabled() const
225     {
226         return bool.init; // TODO: implement
227     }
228 
229     public void setDepthTestingEnabled(bool enabled)
230     {
231         depthStencilDescriptor.depthWriteEnabled = enabled;
232     }
233     public void setDepthTestingFunction(HipDepthTestingFunction d)
234     {
235         assert(HipRenderer.isDepthTestingEnabled, "DepthTesting must be enabled before setting the function.");
236         depthStencilDescriptor.depthCompareFunction = d.fromHipDepthTestingFunction;
237         cmdEncoder.setDepthStencilState(
238             device.newDepthStencilStateWithDescriptor(depthStencilDescriptor)
239         );
240     }
241     public void setStencilTestingEnabled(bool bEnable)
242     {
243     }
244 
245     public void setStencilTestingMask(uint mask)
246     {
247     }
248     public void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a)
249     {
250         
251     }
252 
253     public void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask)
254     {
255     }
256 
257     public void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass)
258     {
259     }
260 
261     public bool hasErrorOccurred(out string err, string file = __FILE__, size_t line = __LINE__)
262     {
263         return bool.init; // TODO: implement
264     }
265 
266     public void begin()
267     {
268         cmdBuffer = cmdQueue.defaultCommandBuffer();
269         cmdBuffer.label = "HipremeRenderer".ns;
270         cmdEncoder = cmdBuffer.renderCommandEncoderWithDescriptor(view.currentRenderPassDescriptor);
271         renderPassDescriptor = view.currentRenderPassDescriptor;
272     }
273 
274     public void setRendererMode(HipRendererMode mode)
275     {
276         final switch(mode)
277         {
278             case HipRendererMode.LINE: 
279                 primitiveType = MTLPrimitiveType.Line; break;
280             case HipRendererMode.LINE_STRIP: 
281                 primitiveType = MTLPrimitiveType.LineStrip; break;
282             case HipRendererMode.POINT: 
283                 primitiveType = MTLPrimitiveType.Point; break;
284             case HipRendererMode.TRIANGLES:
285                 primitiveType = MTLPrimitiveType.Triangle; break;
286             case HipRendererMode.TRIANGLE_STRIP:
287                 primitiveType = MTLPrimitiveType.TriangleStrip; break;
288         }
289     }
290 
291     public void drawIndexed(index_t count, uint offset = 0)
292     {
293         enum IndexType = is(index_t == ushort) ? MTLIndexType.UInt16 : MTLIndexType.UInt32;
294         cmdEncoder.drawIndexedPrimitives(primitiveType, count, IndexType, boundIndexBuffer, offset*index_t.sizeof);
295 
296     }
297 
298     public void drawVertices(index_t count, uint offset = 0)
299     {
300         cmdEncoder.drawPrimitives(primitiveType, offset, count);
301     }
302 
303     public void end()
304     {
305         cmdEncoder.endEncoding();
306         cmdBuffer.presentDrawable(view.currentDrawable);
307         cmdBuffer.commit();
308         cmdBuffer.waitUntilCompleted();
309     }
310 
311     public void clear()
312     {
313         renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadAction.Clear;
314         cmdEncoder.endEncoding();
315         cmdBuffer.commit();
316         begin();
317     }
318 
319     public void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255)
320     {
321         // setColor(r,g,b,a);
322         // renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadAction.Clear;
323         // cmdEncoder.endEncoding();
324         // cmdBuffer.commit();
325         // begin();
326     }
327 
328     public void dispose()
329     {
330         
331     }
332 }